﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace WPFByCoreApp
{
    //public static class MiniDump1
    //{
    //    [Flags]
    //    public enum Option : uint
    //    {
    //        // From dbghelp.h:
    //        Normal = 0x00000000,
    //        WithDataSegs = 0x00000001,
    //        WithFullMemory = 0x00000002,
    //        WithHandleData = 0x00000004,
    //        FilterMemory = 0x00000008,
    //        ScanMemory = 0x00000010,
    //        WithUnloadedModules = 0x00000020,
    //        WithIndirectlyReferencedMemory = 0x00000040,
    //        FilterModulePaths = 0x00000080,
    //        WithProcessThreadData = 0x00000100,
    //        WithPrivateReadWriteMemory = 0x00000200,
    //        WithoutOptionalData = 0x00000400,
    //        WithFullMemoryInfo = 0x00000800,
    //        WithThreadInfo = 0x00001000,
    //        WithCodeSegs = 0x00002000,
    //        WithoutAuxiliaryState = 0x00004000,
    //        WithFullAuxiliaryState = 0x00008000,
    //        WithPrivateWriteCopyMemory = 0x00010000,
    //        IgnoreInaccessibleMemory = 0x00020000,
    //        ValidTypeFlags = 0x0003ffff,
    //    }

    //    enum ExceptionInfo
    //    {
    //        None,
    //        Present
    //    }

    //    //typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
    //    //    DWORD ThreadId;
    //    //    PEXCEPTION_POINTERS ExceptionPointers;
    //    //    BOOL ClientPointers;
    //    //} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
    //    [StructLayout(LayoutKind.Sequential, Pack = 4)]  // Pack=4 is important! So it works also for x64!
    //    struct MiniDumpExceptionInformation
    //    {
    //        public uint ThreadId;
    //        public IntPtr ExceptionPointers;
    //        [MarshalAs(UnmanagedType.Bool)]
    //        public bool ClientPointers;
    //    }

    //    //BOOL
    //    //WINAPI
    //    //MiniDumpWriteDump(
    //    //    __in HANDLE hProcess,
    //    //    __in DWORD ProcessId,
    //    //    __in HANDLE hFile,
    //    //    __in MINIDUMP_TYPE DumpType,
    //    //    __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
    //    //    __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    //    //    __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
    //    //    );
    //    //// Overload requiring MiniDumpExceptionInformation
    //    //[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]

    //    //static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);

    //    //// Overload supporting MiniDumpExceptionInformation == NULL
    //    //[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
    //    //static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
    //    [DllImport("DbgHelp.dll")]
    //    private static extern Boolean MiniDumpWriteDump(
    //                                IntPtr hProcess,
    //                                Int32 processId,
    //                                IntPtr fileHandle,
    //                                MiniDumpType dumpType,
    //                                ref MinidumpExceptionInfo excepInfo,
    //                                IntPtr userInfo,
    //                                IntPtr extInfo);
    //    struct MinidumpExceptionInfo
    //    {
    //        public Int32 ThreadId;
    //        public IntPtr ExceptionPointers;
    //        public Boolean ClientPointers;
    //    }
    //    public enum MiniDumpType
    //    {
    //        None = 0x00010000,
    //        Normal = 0x00000000,
    //        WithDataSegs = 0x00000001,
    //        WithFullMemory = 0x00000002,
    //        WithHandleData = 0x00000004,
    //        FilterMemory = 0x00000008,
    //        ScanMemory = 0x00000010,
    //        WithUnloadedModules = 0x00000020,
    //        WithIndirectlyReferencedMemory = 0x00000040,
    //        FilterModulePaths = 0x00000080,
    //        WithProcessThreadData = 0x00000100,
    //        WithPrivateReadWriteMemory = 0x00000200,
    //        WithoutOptionalData = 0x00000400,
    //        WithFullMemoryInfo = 0x00000800,
    //        WithThreadInfo = 0x00001000,
    //        WithCodeSegs = 0x00002000
    //    }

    //    [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
    //    static extern uint GetCurrentThreadId();

    //    static bool Write(SafeHandle fileHandle, Option options, ExceptionInfo exceptionInfo)
    //    {
    //        Process currentProcess = Process.GetCurrentProcess();
    //        IntPtr currentProcessHandle = currentProcess.Handle;
    //        uint currentProcessId = (uint)currentProcess.Id;
    //        MiniDumpExceptionInformation exp;
    //        exp.ThreadId = GetCurrentThreadId();
    //        exp.ClientPointers = false;
    //        exp.ExceptionPointers = IntPtr.Zero;
    //        if (exceptionInfo == ExceptionInfo.Present)
    //        {
    //            exp.ExceptionPointers = Marshal.GetExceptionPointers();
    //        }

    //        MinidumpExceptionInfo mei = new MinidumpExceptionInfo();

    //        mei.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
    //        mei.ExceptionPointers = Marshal.GetExceptionPointers();
    //        mei.ClientPointers = true;
    //        // return exp.ExceptionPointers == IntPtr.Zero ? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) : MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero);
    //        if (exp.ExceptionPointers == IntPtr.Zero)
    //        {
    //            MiniDumpWriteDump(
    //                                currentProcessHandle,
    //                                currentProcess.Id,
    //                                fileHandle.DangerousGetHandle(),
    //                                MiniDump.MiniDumpType.WithFullMemory,
    //                                ref mei,
    //                                IntPtr.Zero,
    //                                IntPtr.Zero);
    //        }


    //        return true;
    //    }

    //    static bool Write(SafeHandle fileHandle, Option dumpType)
    //    {
    //        return Write(fileHandle, dumpType, ExceptionInfo.None);
    //    }

    //    public static Boolean TryDump(String dmpPath, Option dmpType = Option.Normal)
    //    {
    //        string path = "";
    //        if (string.IsNullOrEmpty(dmpPath))
    //        {
    //            dmpPath = "\\" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-ms") + ".dmp";
    //            path = Path.Combine(Environment.CurrentDirectory, dmpPath);
    //        }
    //        else
    //        {
    //            path = dmpPath;
    //        }
    //        var dir = Path.GetDirectoryName(path);

    //        if (dir != null && !Directory.Exists(dir))
    //        {
    //            Directory.CreateDirectory(dir);
    //        }

    //        //只保留5个dump文件
    //        DirectoryInfo root = new DirectoryInfo(dir);
    //        List<FileInfo> files = root.GetFiles().ToList();
    //        while (files != null && files.Count > 4)
    //        {
    //            files = files.OrderBy(a => a.CreationTime).ToList();
    //            File.Delete(files[0].FullName);
    //            files.RemoveAt(0);
    //        }

    //        using (var fs = new FileStream(path, FileMode.Create))
    //        {
    //            return Write(fs.SafeFileHandle, dmpType);
    //        }
    //    }

    //}


    public sealed class MiniDump
    {
        [Flags]
        public enum DumpType : uint
        {
            // From dbghelp.h:
            MiniDumpNormal = 0x00000000,
            MiniDumpWithDataSegs = 0x00000001,
            MiniDumpWithFullMemory = 0x00000002,
            MiniDumpWithHandleData = 0x00000004,
            MiniDumpFilterMemory = 0x00000008,
            MiniDumpScanMemory = 0x00000010,
            MiniDumpWithUnloadedModules = 0x00000020,
            MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
            MiniDumpFilterModulePaths = 0x00000080,
            MiniDumpWithProcessThreadData = 0x00000100,
            MiniDumpWithPrivateReadWriteMemory = 0x00000200,
            MiniDumpWithoutOptionalData = 0x00000400,
            MiniDumpWithFullMemoryInfo = 0x00000800,
            MiniDumpWithThreadInfo = 0x00001000,
            MiniDumpWithCodeSegs = 0x00002000,
            MiniDumpWithoutAuxiliaryState = 0x00004000,
            MiniDumpWithFullAuxiliaryState = 0x00008000,
            MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
            MiniDumpIgnoreInaccessibleMemory = 0x00020000,
            MiniDumpValidTypeFlags = 0x0003ffff,
        };

        //typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
        //    DWORD ThreadId;
        //    PEXCEPTION_POINTERS ExceptionPointers;
        //    BOOL ClientPointers;
        //} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
        [StructLayout(LayoutKind.Sequential, Pack = 4)]  // Pack=4 is important! So it works also for x64!
        struct MiniDumpExceptionInformation
        {
            public uint ThreadId;
            public IntPtr ExceptioonPointers;
            [MarshalAs(UnmanagedType.Bool)]
            public bool ClientPointers;
        }

        //BOOL
        //WINAPI
        //MiniDumpWriteDump(
        //    __in HANDLE hProcess,
        //    __in DWORD ProcessId,
        //    __in HANDLE hFile,
        //    __in MINIDUMP_TYPE DumpType,
        //    __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
        //    __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
        //    __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
        //    );
        [DllImport("dbghelp.dll",
          EntryPoint = "MiniDumpWriteDump",
          CallingConvention = CallingConvention.StdCall,
          CharSet = CharSet.Unicode,
          ExactSpelling = true, SetLastError = true)]
        static extern bool MiniDumpWriteDump(
          IntPtr hProcess,
          uint processId,
          IntPtr hFile,
          uint dumpType,
          ref MiniDumpExceptionInformation expParam,
          IntPtr userStreamParam,
          IntPtr callbackParam);

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
        static extern uint GetCurrentThreadId();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
        static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
        static extern uint GetCurrentProcessId();

        public static bool Write(string fileName)
        {
            return Write(fileName, DumpType.MiniDumpWithFullMemory);
        }
        public static bool Write(string fileName, DumpType dumpType)
        {
            using (var fs = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
            {
                MiniDumpExceptionInformation exp;
                exp.ThreadId = GetCurrentThreadId();
                exp.ClientPointers = false;
                exp.ExceptioonPointers = System.Runtime.InteropServices.Marshal.GetExceptionPointers();
                bool bRet = MiniDumpWriteDump(
                  GetCurrentProcess(),
                  GetCurrentProcessId(),
                  fs.SafeFileHandle.DangerousGetHandle(),
                  (uint)dumpType,
                  ref exp,
                  IntPtr.Zero,
                  IntPtr.Zero);
                return bRet;
            }
        }

        public static Boolean TryDump(String dmpPath)
        {
            string path = "";
            if (string.IsNullOrEmpty(dmpPath))
            {
                dmpPath = "\\" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-ms") + ".dmp";
                path = Path.Combine(Environment.CurrentDirectory, dmpPath);
            }
            else
            {
                path = dmpPath;
            }
            var dir = Path.GetDirectoryName(path);

            if (dir != null && !Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }


            return Write(path);
        }

    }
}
